/**
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


const success: number = 0;
const fail: number = 1;

function main(): int {
  let failures: number = 0;

  failures += check(testBytePerElement(),"test BytePerElement for {{.item.objectType}}");
  failures += check(createDefault(),"Create default empty {{.item.objectType}}");
  failures += check(createEmptyWithLength(),"Create default empty {{.item.objectType}} from length");
  failures += check(createNonEmptyWithLength(),"Create default non empty {{.item.objectType}} from length");
  failures += check(createtypedArrayWithWrongLength(),"Create default non empty {{.item.objectType}} with wrong length");
  failures += check(createEmptyWithIterable(),"Create default empty {{.item.objectType}} from Iterable");
  failures += check(createNonEmptyWithIterable(),"Create default non empty {{.item.objectType}} from Iterable");
  failures += check(testTypedArrayIterator(),"test {{.item.objectType}} Iterator");
  failures += check(testTypedArrayIteratorEmptyArray(), "test {{.item.objectType}} Iterator Empty Array");
  failures += check(testTypedArrayIteratorSingleElement(), "test {{.item.objectType}} Iterator Single Element");
  failures += check(testTypedArrayIteratorAfterModification(), "test {{.item.objectType}} Iterator After Modification");
  failures += check(testTypedArrayIteratorAfterCompletion(), "test {{.item.objectType}} Iterator After Completion");

  failures += check(createtypedArrayWithArrayLike(),"Create {{.item.objectType}} with ArrayLike");

  failures += check(createTAFromEmptyArrayBuffer(),"Create default empty {{.item.objectType}} from empty source");
  failures += check(createTAFromEmptyArrayBufferOneParamNoOffset(),"Create default empty {{.item.objectType}} from empty source one param");
  failures += check(createTAFromEmptyArrayBufferOneOffsetWithWrongNumber(),"Create default empty {{.item.objectType}} from empty source one param");
  failures += check(createTAFromEmptyArrayBufferTwoParams(),"Create default empty {{.item.objectType}} from empty source two params");
  failures += check(createTAFromEmptyArrayBufferTwoParamsWithWrongNumber(),"Create default empty {{.item.objectType}} from empty source two params with wrong number");

  failures += check(createTAFromNonEmptyArrayBuffer(),"Create default empty {{.item.objectType}} from non empty source");
  failures += check(createTAFromNonEmptyArrayBufferOneParamNoOffset(),"Create default empty {{.item.objectType}} from non empty source one param");
  failures += check(createTAFromNonEmptyArrayBufferOneOffsetWithWrongNumber(),"Create default empty {{.item.objectType}} from non empty source one param with wrong number");
  failures += check(createTAFromNonEmptyArrayBufferTwoParams(),"Create default empty {{.item.objectType}} from non empty source two params");
  failures += check(createTAFromNonEmptyArrayBufferTwoParamsWithWrongNumber(),"Create default empty {{.item.objectType}} from non empty source two params with wrong number");

  failures += check(createTAFromNonEmptyArrayBufferOneParamWithOffset(),"Create default empty {{.item.objectType}} from non empty source with offset");
  failures += check(createTAFromNonEmptyArrayBufferOneParamWithOffsetAndSize(),"Create default empty {{.item.objectType}} from non empty source with offset and size");

  failures += check(createTAFromEmptySharedArrayBuffer(),"Create default empty {{.item.objectType}} from empty source");
  failures += check(createTAFromNonEmptySharedArrayBuffer(),"Create default empty {{.item.objectType}} from non empty source");
  failures += check(createTAFromNonEmptySharedArrayBufferOneParamWithOffset(),"Create default empty {{.item.objectType}} from non empty source with offset");
  failures += check(createTAFromNonEmptySharedArrayBufferOneParamWithOffsetAndSize(),"Create default empty {{.item.objectType}} from non empty source with offset and size");

  failures += check(testSliceWithOutParam(), "Try to Array slice() function");
  failures += check(testSliceOneParam(), "Try to Array slice(1) function");
  failures += check(testSliceTwoParams(), "Try to Array slice(2) function");
  failures += check(testSliceTwoParamsWithOtherNumber(), "Try to Array slice(2) With Other Number function");
  failures += check(testSliceOneLengthTwoParams(), "Try to Array slice(2) One Length Two Params function");

  failures += check(testEmptyTypedArrayToString(), "Try to Empty Array toString");
  failures += check(testNonEmptyTypedArrayToString(), "Try to Nonempty Array toString");

  failures += check(testEmptyTypedArrayAt(), "Test to Empty Array at");
  failures += check(testNonEmptyTypedArrayAt(), "Test to Nonempty Array at");

  failures += check(testNonEmptyTypedArrayReverse(), "Test NonEmpty Array Reverse");
  failures += check(testEmptyTypedArrayReverse(), "Test Empty Array Reverse");
  failures += check(testOf(), "Create Array object by means call non-subset {{.item.objectType}}.of(...{{.item.primitiveType}}[])");

  if (failures > 0){
    console.log("failed");
    return fail;
  }

  console.log("All passed");
  return success;
}

function check(result: int, message: String): number {
  if (result == 0) {
    return success;
  }
  console.log("\nFAILED: " + message);
  return fail;
}

function check(result: number, message: String): number {
  if (result == 0) {
    return success;
  }
  console.log("\nFAILED: " + message);
  return fail;
}

const source: {{.item.primitiveType}}[] = {{.item.data}};
const abnormalSource: {{.item.primitiveType}}[] = {{.item.abnormalData}};

function testBytePerElement(): number {
  if ({{.item.objectType}}.BYTES_PER_ELEMENT as int == {{.item.primitiveSizeBytes}}) return success;
  return fail;
}

function createDefault(): number {
  let target: {{.item.objectType}} = new {{.item.objectType}}();
  if (target.length as int == 0 && target.byteOffset as int == 0) return success;
  return fail;
}

function createEmptyWithLength(): number {
  let target: {{.item.objectType}} = new {{.item.objectType}}(0);
  if (target.length as int == 0 && target.byteOffset as int == 0) return success;
  return fail;
}

function createNonEmptyWithLength(): number {
  let target: {{.item.objectType}} = new {{.item.objectType}}(5);
  if (target.length as int == 5 && target.byteOffset as int == 0 && target.byteLength as int == 5*{{.item.primitiveSizeBytes}}){
    return success;
  }
  return fail;
}

function createtypedArrayWithWrongLength(): number {
  try {
    let target: {{.item.objectType}} = new {{.item.objectType}}(-1 as number);
  } catch(e) {
    return success;
  }
  return fail;
}

function createEmptyWithIterable(): number {
  let array: Array<Number> = new Array<Number>();
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(array.values() as Iterable<Number>);
  } catch(e) {
    return fail;
  }

  for (let value of target) {
    console.log(value);
    return fail;
  }

  if (target.length as int == 0){
    return success;
  }
  return fail;
}

function createNonEmptyWithIterable(): number {
  let array: Array<Number> = new Array<Number>();
  array.push(1);
  array.push(2);
  array.push(3);
  let target: {{.item.objectType}} = new {{.item.objectType}}(array.values() as Iterable<Number>);

  if (target.length as int == 3 && target.byteOffset as int == 0 && target.byteLength as int == 3*{{.item.primitiveSizeBytes}}){
    return success;
  }

  let exceptArray: Array<Number> = new Array<Number>();
  for (let value of target) {
    exceptArray.push(value);
    console.log(value);
  }

  let origin: {{.item.objectType}} = new {{.item.objectType}}(exceptArray.values() as Iterable<Number>);
  for (let i: int = 0; i< target.length as int; i++) {
    let tv = target[i] as number;
    let ov = origin[i] as number;
    console.log(tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }
  return fail;
}

function testTypedArrayIterator(): number {
  let ss = new ArrayBuffer(5 * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let length = typedArray.length;

  try {
    typedArray[0] = 1 as number;
    typedArray[1] = 2 as number;
    typedArray[2] = 3 as number;
    typedArray[3] = 4 as number;
    typedArray[4] = 5 as number;
  } catch(e) {
    console.log(e);
    return fail;
  }

  let arrayIterator = typedArray.$_iterator();
  let next = arrayIterator.next();
  let counter: int = 0;
  while (!next.done) {
    if (next.value == undefined) {
        return fail;
    }
    if (typedArray[counter] as number != (next.value) as number) {
        return fail;
    }
    next = arrayIterator.next()
    counter++
  }

  if (counter != typedArray.length) {
    return fail;
  }

  return success;
}

function testTypedArrayIteratorEmptyArray(): number {
  let emptyArray = new {{.item.objectType}}(new ArrayBuffer(0));
  let emptyIterator = emptyArray.$_iterator();
  let emptyNext = emptyIterator.next();

  if (!emptyNext.done || emptyNext.value != undefined) {
    return fail;
  }

  return success;
}

function testTypedArrayIteratorSingleElement(): number {
  let singleArray = new {{.item.objectType}}(new ArrayBuffer(1 *  {{.item.primitiveSizeBytes}}));

  try {
    singleArray[0] = 99;
  } catch(e) {
    console.log(e);
    return fail;
  }

  let singleIterator = singleArray.$_iterator();
  let singleNext = singleIterator.next();

  if (singleNext.done || singleNext.value != 99) {
    return fail;
  }

  singleNext = singleIterator.next();
  if (!singleNext.done || singleNext.value != undefined) {
    return fail;
  }

  return success;
}

function testTypedArrayIteratorAfterModification(): number {
  let ss = new ArrayBuffer(5 * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  try {
    typedArray[0] = 1;
    typedArray[1] = 2;
    typedArray[2] = 3;
    typedArray[3] = 4;
    typedArray[4] = 5;
  } catch(e) {
    console.log(e);
    return fail;
  }

  // Testing iterator after modification
  let arrayIterator = typedArray.$_iterator();
  let next = arrayIterator.next();
  let expectedValues = [1, 2, 3, 4, 5];
  let counter: int = 0;

  while (!next.done) {
    if (next.value == undefined) {
      return fail;
    }
    if (expectedValues[counter] != next.value) {
      return fail;
    }
    next = arrayIterator.next();
    counter++;
  }

  if (counter != typedArray.length) {
    return fail;
  }

  // Modify a value
  typedArray[0] = 99;
  if(typedArray.$_iterator().next().value != 99){
    return fail;
  }

  return success;
}

function testTypedArrayIteratorAfterCompletion(): number {
  let ss = new ArrayBuffer(5 * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  typedArray[0] = 1;
  typedArray[1] = 2;
  typedArray[2] = 3;
  typedArray[3] = 4;
  typedArray[4] = 5;

  let arrayIterator = typedArray.$_iterator();
  let next = arrayIterator.next();

  while (!next.done) {
    next = arrayIterator.next();
  }

  // Check iterator after completion
  next = arrayIterator.next();
  if (!next.done || next.value != undefined) {
    return fail;
  }

  return success;
}

function createtypedArrayWithArrayLike(): number {
  let array = new Array<Number>();
  array.push(1 as Number);
  array.push(2 as Number);
  array.push(3 as Number);

  let target: {{.item.objectType}} = new {{.item.objectType}} (array as ArrayLike<Number> );
  if (target.length as int == 3 && target.byteOffset as int == 0){
    return success;
  }
  return fail;
}


function createTAFromEmptyArrayBuffer(): number {
  let ss = new ArrayBuffer(0);  // Buffer 0-bytes length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss);
  } catch(e) {
    return fail;
  }

  if (target.length as int == 0 && target.byteOffset as int == 0) return success;
  return fail;
}

function createTAFromNonEmptyArrayBuffer(): number {
  let ss = new ArrayBuffer(5*{{.item.primitiveSizeBytes}});  // Buffer 5-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss);
  } catch(e) {
    return fail;
  }

  if (target.byteLength as int == 5*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 0) return success;
  console.println("Error: Actual bytes length: " + target.byteLength);
  return fail;
}

function createTAFromEmptySharedArrayBuffer(): number {
  let ss = new SharedArrayBuffer(0);  // Buffer 0-bytes length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss);
  } catch(e) {
    return fail;
  }

  if (target.length as int == 0 && target.byteOffset as int == 0) return success;
  return fail;
}

function createTAFromNonEmptySharedArrayBuffer(): number {
  let ss = new SharedArrayBuffer(5*{{.item.primitiveSizeBytes}} as int);  // Buffer 5-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss);
  } catch(e) {
    return fail;
  }

  if (target.byteLength as int == 5*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 0) return success;
  console.println("Error: Actual bytes length: " + target.byteLength);
  return fail;
}



function createTAFromEmptyArrayBufferOneParamNoOffset(): number {
  let ss = new ArrayBuffer(0);
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 0);
  } catch(e) {
    return fail;
  }
  if (target.length as number != 0 || target.byteOffset as number != 0 ) {
    return fail;
  }

  try {
    target = new {{.item.objectType}}(ss, undefined);
  } catch(e) {
    return fail;
  }

  if (target.length as number != 0 || target.byteOffset as number != 0) {
    return fail;
  }

  return success;
}

function createTAFromEmptyArrayBufferOneOffsetWithWrongNumber(): number {
  let ss = new ArrayBuffer(0);
  let target: {{.item.objectType}};
  let result: number = success;

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -1);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, 1);
  } catch(e) {
    result = success;
  }

  return result;
}


function createTAFromNonEmptyArrayBufferOneParamNoOffset(): number {
  let ss = new ArrayBuffer(5*{{.item.primitiveSizeBytes}});  // Buffer 5-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 0);
  } catch(e) {
    return fail;
  }

  if (target.byteLength as number != 5*{{.item.primitiveSizeBytes}} || target.byteOffset as number != 0
    || target.length as number != 5){
    console.println("Error: Actual bytes length: " + target.byteLength);
    return fail;
  }

  try {
    target = new {{.item.objectType}}(ss, undefined);
  } catch(e) {
    return fail;
  }

  if (target.byteLength as number != 5*{{.item.primitiveSizeBytes}} || target.byteOffset as number != 0
    || target.length as number != 5){
    console.println("Error: Actual bytes length: " + target.byteLength);
    return fail;
  }

  return success;
}

function createTAFromNonEmptyArrayBufferOneOffsetWithWrongNumber(): number {
  let ss = new ArrayBuffer(5*{{.item.primitiveSizeBytes}});  // Buffer 5-items length;
  let target: {{.item.objectType}};
  let result: number = success;
  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -1);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, 6*{{.item.primitiveSizeBytes}});
  } catch(e) {
    result = success;
  }

  return result;
}

function createTAFromNonEmptyArrayBufferOneParamWithOffset(): number {
  let ss = new ArrayBuffer(7*{{.item.primitiveSizeBytes}});  // Buffer 7-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 2*{{.item.primitiveSizeBytes}});
  } catch(e) {
    console.println(e);
    return fail;
  }

  if (target.byteLength as int == 5*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 2*{{.item.primitiveSizeBytes}}
    && target.length as int == 5){
    return success;
  }
  console.println("Error: Actual bytes length: " + target.byteLength);
  console.println("Error: Actual bytes offset: " + target.byteOffset);
  console.println("Error: Actual length: " + target.length);
  return fail;
}

function createTAFromNonEmptyArrayBufferOneParamWithOffsetAndSize(): number {
  let ss = new ArrayBuffer(7*{{.item.primitiveSizeBytes}});  // Buffer 7-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 2*{{.item.primitiveSizeBytes}}, 4);
  } catch(e) {
    console.println(e);
    return fail;
  }

  if (target.byteLength as int == 4*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 2*{{.item.primitiveSizeBytes}} && target.length as int == 4) return success;
  console.println("Error: Actual bytes length: " + target.byteLength);
  console.println("Error: Actual bytes offset: " + target.byteOffset);
  console.println("Error: length: " + target.length);
  return fail;
}

function createTAFromNonEmptySharedArrayBufferOneParamWithOffset(): number {
  let ss = new SharedArrayBuffer(7*{{.item.primitiveSizeBytes}} as int);  // Buffer 7-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 2*{{.item.primitiveSizeBytes}});
  } catch(e) {
    console.println(e);
    return fail;
  }

  if (target.byteLength as int == 5*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 2*{{.item.primitiveSizeBytes}}
    && target.length as int == 5){
    return success;
  }
  console.println("withSharedArrayBuffer1 Error: Actual bytes length: " + target.byteLength);
  console.println("withSharedArrayBuffer1 Error: Actual bytes offset: " + target.byteOffset);
  console.println("withSharedArrayBuffer1 Error: Actual length: " + target.length);
  return fail;
}

function createTAFromNonEmptySharedArrayBufferOneParamWithOffsetAndSize(): number {
  let ss = new SharedArrayBuffer(7*{{.item.primitiveSizeBytes}} as int);  // Buffer 7-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 2*{{.item.primitiveSizeBytes}}, 4);
  } catch(e) {
    console.println(e);
    return fail;
  }

  if (target.byteLength as int == 4*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 2*{{.item.primitiveSizeBytes}} && target.length as int == 4) return success;
  console.println("withSharedArrayBuffer2 Error: Actual bytes length: " + target.byteLength);
  console.println("withSharedArrayBuffer2 Error: Actual bytes offset: " + target.byteOffset);
  console.println("withSharedArrayBuffer2 Error: length: " + target.length);
  return fail;
}


function createTAFromEmptyArrayBufferTwoParams(): number {
  let ss = new ArrayBuffer(0);
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 0, 0);
  } catch(e) {
    return fail;
  }

  if (target.length as number != 0 || target.byteOffset as number != 0) {
    return fail;
  }

  try {
    target = new {{.item.objectType}}(ss, undefined, undefined);
  } catch(e) {
    return fail;
  }

  if (target.length as number != 0 || target.byteOffset as number != 0) {
    return fail;
  }

  return success;
}

function createTAFromNonEmptyArrayBufferTwoParams(): number {
  let ss = new ArrayBuffer(5 * {{.item.primitiveSizeBytes}});  // Buffer 5-items length;
  let target: {{.item.objectType}};
  try {
    target = new {{.item.objectType}}(ss, 0, 5);
  } catch(e) {
    return fail;
  }

  if (target.byteLength as int == 5*{{.item.primitiveSizeBytes}} && target.byteOffset as int == 0 && target.length == 5){
    return success;
  }
  console.println("Error: Actual bytes length: " + target.byteLength);
  return fail;
}

function createTAFromEmptyArrayBufferTwoParamsWithWrongNumber(): number {
  let ss = new ArrayBuffer(0);  // Buffer 5-items length;
  let target: {{.item.objectType}};
  let result: number = success;
  try {
    result = fail;
    target = new {{.item.objectType}}(ss, new Number(-1), undefined);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, new Number(2), undefined);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -1, -1);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -2, 66);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, 2, 5);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, 2, 66);
  } catch(e) {
    result = success;
  }

  return result;
}

function createTAFromNonEmptyArrayBufferTwoParamsWithWrongNumber(): number {
  let ss = new ArrayBuffer(5 * {{.item.primitiveSizeBytes}});  // Buffer 5-items length;
  let target: {{.item.objectType}};
  let result: number = success;

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, new Number(-1), undefined);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -2, 66);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -1, -1);
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, -1, 6 * {{.item.primitiveSizeBytes}});
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, 2, 6 * {{.item.primitiveSizeBytes}});
  } catch(e) {
    result = success;
  }

  try {
    result = fail;
    target = new {{.item.objectType}}(ss, undefined, new Number(-1));
  } catch(e) {
    result = success;
  }

  return result;
}


function testSliceWithOutParam(): int {
  //let source: {{.item.primitiveType}}[] = [10, 20, 30, 40, 50, 60];
  let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}});

  let origin: {{.item.objectType}};

  try {
    origin = new {{.item.objectType}}(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }


  let target: {{.item.objectType}};

  try {
    target = origin.slice();
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int) {
    console.log("Array length mismatch on slice");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = 0; i< origin.length as int; i++) {
    let tv = target[i] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  origin= new {{.item.objectType}}(0);
  if (origin.length as int != 0){
    return fail;
  }

  try {
    target = origin.slice();
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != 0){
    return fail;
  }
  return success;
}

function testSliceOneParam(): int {
  //let source: {{.item.primitiveType}}[] = [10, 20, 30, 40, 50, 60];
  let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}});

  let origin: {{.item.objectType}};

  try {
    origin = new {{.item.objectType}}(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let sliceStart: int = 1;
  let sliceEnd: int = origin.length as int;

  let target: {{.item.objectType}};

  try {
    target = origin.slice(sliceStart);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int - sliceStart) {
    console.log("Array length mismatch on slice One Params" + target.length);
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  sliceStart = 0;
  try {
    target = origin.slice(undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != origin.length as int) {
    console.log("Array length mismatch on slice One Params" + target.length);
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  return success;
}

function testSliceTwoParams(): int {
  //let source: {{.item.primitiveType}}[] = [10, 20, 30, 40, 50, 60, 70, 80];
  let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}});

  let origin: {{.item.objectType}};

  try {
    origin = new {{.item.objectType}}(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let sliceStart: int = 2;
  let sliceEnd: int = 4;

  let target: {{.item.objectType}};

  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != sliceEnd - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  sliceStart = 0;
  sliceEnd = origin.length as int;
  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != sliceEnd - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.slice(new Number(sliceStart), undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != sliceEnd - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.slice(undefined, undefined);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != sliceEnd - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.slice(undefined, new Number(sliceEnd));
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != sliceEnd - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = sliceStart; i< sliceEnd; i++) {
    let tv = target[i - sliceStart] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  try {
    target = origin.slice(0, 0);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  return success;
}

function testSliceTwoParamsWithOtherNumber(): int {
  //let source: {{.item.primitiveType}}[] = [10, 20, 30, 40, 50, 60, 70, 80];
  let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}});

  let origin: {{.item.objectType}};

  try {
    origin = new {{.item.objectType}}(ss);
    origin.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let sliceStart: int = 4;
  let sliceEnd: int = 2;

  let target: {{.item.objectType}};

  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  sliceStart = -1;
  sliceEnd = origin.length as int;
  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != sliceEnd - (origin.length + sliceStart)) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  //Check all the data copied;
  for (let i: int = (origin.length + sliceStart) as int; i< sliceEnd; i++) {
    let tv = target[(i - (origin.length + sliceStart)) as int] as {{.item.primitiveType}};
    let ov = origin[i] as {{.item.primitiveType}};
    console.log(source[i] + "->" + tv + "->" + ov);
    if (tv != ov) {
      console.log("Array data mismatch");
      return fail;
    }
  }

  sliceStart = 0;
  sliceEnd = -origin.length as int;
  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    console.log(e);
    return fail;
  }

  if (target.length as int != (origin.length + sliceEnd) - sliceStart) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  return success;
}

function testSliceOneLengthTwoParams(): int {
  let source1: {{.item.primitiveType}}[] = [10];
  let ss = new ArrayBuffer(source1.length as int * {{.item.primitiveSizeBytes}});

  let origin: {{.item.objectType}};

  try {
    origin = new {{.item.objectType}}(ss);
    origin.set(source1);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let sliceStart: int = 4;
  let sliceEnd: int = 2;

  let target: {{.item.objectType}};

  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  sliceStart = 2;
  sliceEnd = 4;
  try {
    target = origin.slice(sliceStart, sliceEnd);
  } catch(e) {
    return fail;
  }

  if (target.length as int != 0) {
    console.log("Array length mismatch on slice2");
    return fail;
  }

  return success;
}

function testEmptyTypedArrayToString(): number {
  let ss = new ArrayBuffer(0);
  let typedArray: {{.item.objectType}};
  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  const toStringResult = typedArray.toString();
  console.log("toString result: " + toStringResult);
  // Assertion
  if (toStringResult == "") {
    console.log("testEmptyTypedArrayToString test passed.");
    return success;
  } else {
    console.log("toString test failed.");
    return fail;
  }
}

function testNonEmptyTypedArrayToString(): number {
  let source1: number[] = [1, 2, 3, 4, 5];
  let ss = new ArrayBuffer(source1.length * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
    typedArray.set(source1);
  } catch(e) {
    console.log(e);
    return fail;
  }

  const toStringResult = typedArray.toString();
  console.log("toString result: " + toStringResult);
  // Assertion
  if (toStringResult == "1,2,3,4,5") {
    console.log("toString test passed.");
    return success;
  } else {
    console.log("testNonEmptyTypedArrayToString test failed.");
    return fail;
  }
}

function testNonEmptyTypedArrayAt(): number {
  //let source: number[] = [1, 2, 3, 4, 5];
  let ss = new ArrayBuffer(source.length * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
    typedArray.set(source);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let index = 0;
  let length = typedArray.length;
  let atResult = typedArray.at(index);
  console.log("testNonEmptyTypedArrayAt result: " + atResult);
  // Assertion
  if (atResult == new Number(source[index])) {
    console.log("testNonEmptyTypedArrayAt test passed.");
  } else {
    console.log("testNonEmptyTypedArrayAt test failed.");
    return fail;
  }

  index = -1;
  atResult = typedArray.at(index);
  console.log("testNonEmptyTypedArrayAt result: " + atResult);
  // Assertion
  if (atResult == new Number(source[index + length as int])) {
    console.log("testNonEmptyTypedArrayAt test passed.");
  } else {
    console.log("testNonEmptyTypedArrayAt test failed.");
    return fail;
  }

  index = typedArray.length as int;
  atResult = typedArray.at(index);
  console.log("testNonEmptyTypedArrayAt result: " + atResult);
  // Assertion
  if (atResult == undefined) {
    console.log("testNonEmptyTypedArrayAt test passed.");
  } else {
    console.log("testNonEmptyTypedArrayAt test failed.");
    return fail;
  }
  return success;
}

function testEmptyTypedArrayAt(): number {
  let ss = new ArrayBuffer(0);
  let typedArray: {{.item.objectType}};
  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  let atResult = typedArray.at(0);
  console.log("testEmptyTypedArrayAt result: " + atResult);
  // Assertion
  if (atResult == undefined) {
    console.log("testEmptyTypedArrayAt test passed.");
    return success;
  } else {
    console.log("testEmptyTypedArrayAt test failed.");
    return fail;
  }
}

function testNonEmptyTypedArrayReverse(): number {
  let source1: {{.item.primitiveType}}[] = [5, 3, 1, 4, 2];
  let ss = new ArrayBuffer(source1.length * {{.item.primitiveSizeBytes}});
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
    typedArray.set(source1);
  } catch(e) {
    console.log(e);
    return fail;
  }

  // Test toReversed function
  let reversedArray: {{.item.objectType}} = typedArray.reverse();
  let expectedArray: {{.item.primitiveType}}[] = [2, 4, 1, 3, 5];
  for (let i = 0; i < reversedArray.length; i++) {
    if (reversedArray[i] != expectedArray[i]) {
      console.log("Test failed. testNonEmptyTypedArrayReverse: " + JSON.stringify(reversedArray[i]));
      return fail;
    }
  }
  return success;
}

function testEmptyTypedArrayReverse(): number {
  let ss = new ArrayBuffer(0);
  let typedArray: {{.item.objectType}};

  try {
    typedArray = new {{.item.objectType}}(ss);
  } catch(e) {
    console.log(e);
    return fail;
  }

  // Test toReversed function
  let reversedArray: {{.item.objectType}} = typedArray.reverse();

  if (reversedArray != typedArray || reversedArray.length != 0) {
    console.log("Test failed. testEmptyTypedArrayReverse: " + reversedArray);
    return fail;
  }

  return success;
}

function testOf(): int {
  let origin: {{.item.objectType}};

  try {
    origin = {{.item.objectType}}.of(...source);
  } catch(e) {
    console.println(e);
    return fail;
  }

  if (origin.length != source.length) {
    console.println("Array length mismatch");
    return fail;
  }

  for (let i: int = 0; i < source.length; i++) {
    if (origin[i] as {{.item.primitiveType}} != source[i]) {
      console.println("Array data mismatch");
      return fail;
    }
  }
  return success;
}
